/*
*  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
*  reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*  1. Redistributions of source code must retain the above copyright
*  notice, this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright
*  notice, this list of conditions and the following disclaimer in
*  the documentation and/or other materials provided with the
*  distribution.
*
*  3. The end-user documentation included with the redistribution,
*  if any, must include the following acknowledgment:
*  "This product includes software developed by the
*  Sun Microsystems, Inc. for Project JXTA."
*  Alternately, this acknowledgment may appear in the software itself,
*  if and wherever such third-party acknowledgments normally appear.
*
*  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA"
*  must not be used to endorse or promote products derived from this
*  software without prior written permission. For written
*  permission, please contact Project JXTA at http://www.jxta.org.
*
*  5. Products derived from this software may not be called "JXTA",
*  nor may "JXTA" appear in their name, without prior written
*  permission of Sun.
*
*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
*  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
*  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
*  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
*  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
*  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
*  SUCH DAMAGE.
*  ====================================================================
*
*  This software consists of voluntary contributions made by many
*  individuals on behalf of Project JXTA.  For more
*  information on Project JXTA, please see
*  <http://www.jxta.org/>.
*
*  This license is based on the BSD license adopted by the Apache Foundation.
*
*  $Id: JxtaNode.java,v 1.7 2006/10/03 18:22:07 nano Exp $
*/

package net.jxta.myjxta.util.objectmodel;

import net.jxta.myjxta.plugin.ISelectableNode;
import net.jxta.myjxta.plugin.SelectionNodeType;
import net.jxta.myjxta.util.JxtaNodeListener;
import net.jxta.myjxta.util.Resources;

import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.util.*;

/**
 * The super class for all JxtaNode instances
 * It also represents the root node
 *
 * @version $Id: JxtaNode.java,v 1.7 2006/10/03 18:22:07 nano Exp $
 *
 * @author james todd [gonzo at jxta dot org]
 */

public class JxtaNode
implements Comparable, TreeNode,ISelectableNode {
    
    /** The value for possible  category of this node */
    public static final String CATEGORY_ROOT = "root";
    public static final char DELIMITER = '/';
    public static final char ESCAPE = '\\';

    /** A default description for the node */
    protected static final String DESCRIPTION = "";

    /** The root node type */
    protected final String category;

    /** A unique id of this node  */
    private String id = null;

    /** The label of this node */
    private String label = null;

    /** The parent of this node */
    private JxtaNode parent = null;

    /** The list of children in this node */
    private List<JxtaNode> children = null;
    
    protected List<JxtaNodeListener> nodeListeners=null;

//    nano@jxta.org: no usage so i commented it out
//    /**
//    * Some nodes can  contain to more than one parents.
//    * This is the list of parents this node belongs to.
//    */
//    private List parentList = null;
    
    private final ImageIcon rootIcon =
        Resources.getInstance().getIconResource("TreeTable.RootElement");

    /**
     * Create a new JxtaNode. The id and the label are both set to
     * the supplied value of id
     * 
     * @param id the id for this node 
     * @param parent the parent for this node 
     */
    public JxtaNode(JxtaNode parent, String id) {
        this(parent, id, id, CATEGORY_ROOT);
    }

    /**
     * Create a new JxtaNode
     *
     * @param parent the parent of this node
     * @param id the unique id of this node 
     * @param label the descriptive label of this node
     * @param category the category of this node
     */
    public JxtaNode(JxtaNode parent, String id, String label, String category) {
        this.parent = parent;
        this.id = id;
        this.label = label;
        this.category = category;
    }

    public void addListener(JxtaNodeListener listener){
    	if (nodeListeners==null){
    		nodeListeners=new ArrayList<JxtaNodeListener>();
    	}
    	if (!nodeListeners.contains(listener)){
    		nodeListeners.add(listener);
    	}
    }
    
    public void removeListener(JxtaNodeListener listener){
    	if (nodeListeners!=null)
    		nodeListeners.remove(listener);
    }
    
    protected void informListeners(){
    	if (nodeListeners!=null){
            for (JxtaNodeListener listener : nodeListeners) {
                listener.nodeChanged(this);
            }
        }
    }
    /**
    * Return the parent node of this node 
    * 
    * @return the parent of this node
    */
    public JxtaNode getParent() {
//    public TreeNode getParent() {
        return this.parent;
    }

    /**
     * Set the parent of this node 
     *
     * @param parent the new parent of this node
     */
    public void setParent(JxtaNode parent) {
        this.parent = parent;
        
        parent.add(this);
    }

    /**
    * Return the id of this node 
    * 
    * @return the id of this node
    */
    public String getId() {
        return this.id;
    }

    /**
     * Return the category of this node 
     * 
     * @return the category of this node 
     */
    public String getCategory() {
        return this.category;
    }

    /**
     * Checks whether this node is a leaf. It is a leaf if this node does not
     * have any children
     * 
     * @return true if this node is a lead, false otherwise
     */
    public boolean isLeaf() {
        return ((this.children == null ||
            (this.children != null &&
            this.children.size() == 0)) ? true : false);
    }

    public boolean getAllowsChildren() {
        return true;
    }

    public int getChildCount() {
        return this.children != null ? this.children.size() : 0;
    }
    /**
     * Return the number of children of this node
     *
     * @return the number of child nodes
     */
    public int getChildrenCount() {
        return getChildCount();
    }

    /**
     * Get an iteration of the child nodes 
     *
     * @return a list of the child nodes
     */
    public Iterator getChildren() {
        return this.children != null ?
               this.children.iterator() : Collections.EMPTY_LIST.iterator();
    }

    public Enumeration children() {
        return this.children != null ?
            Collections.enumeration(this.children) :
            Collections.enumeration(Collections.EMPTY_LIST);
    }

    /**
     * Gets the index of the indicated child in the indicated node
     * 
     * @return the index of the indicated child 
     * 
     */
    public int getIndex(TreeNode child) {
        return this.children != null ? this.children.indexOf(child) : -1;
    }
    
    public int getIndexOf(JxtaNode child) {
        return getIndex(child);
    }

    /**
     * Return the child at the indicated index
     *
     * @param index the index for which to get the child
     * @return the child at the indicated index
     */
    public TreeNode getChildAt(int index) {
        return this.children != null ?
            this.children.get(index) : (JxtaNode)null;
    }

    /**
     * Add a child node to this node
     *
     * @param child the child to add
     */
    public void add(JxtaNode child) {
        if (child.getParent() == null) {
            child.setParent(this);
        }
        
        if (getIndexOf(child) == -1) {
            boolean added = false;
            
            for (int i = 0; i < getChildrenCount() && ! added; i++) {
                if (((JxtaNode)getChildAt(i)).compareTo(child) >= 0) {
                    this.children.add(i, child);
                    added = true;
                }
            }
            
            if (! added) {
                if (this.children == null) {
                    this.children = new ArrayList<JxtaNode>();
                }
                
                this.children.add(child);
            }
        }
    }

    /**
     * Removes the indicated node from the list of children
     */
    public void remove(JxtaNode child) {
        // remove the child from the list  - if the List object
        // does not remove, we cannot remove the child and just do nothing
        if (getIndexOf(child) > -1) {
            try {
                this.children.remove(child);
            } catch(Exception iox) {
                iox.printStackTrace();
            }
        }
    }

    /**
     * Remove all children from this node 
     */
    public void removeAll() {
        for (Enumeration e = children(); e.hasMoreElements(); ) {
            remove((JxtaNode)e.nextElement());
        }
        
        this.children = null;
    }

    /**
     * Get the path list that leads to this node
     * 
     * @return the path to this node 
     */
    public JxtaNode[] getPath() {
        return getPathToRoot(this, 0);
    }

    public String getNormalizedPath() {
        JxtaNode n = this;
        StringBuffer sb = new StringBuffer(DELIMITER + n.getId());
        
        while ((n = n.getParent()) != null) {
            sb.insert(0, DELIMITER + n.getId());
        }
        /*
        StringBuffer sb = new StringBuffer();
        JxtaNode[] p = getPath();
        String s = null;
        StringBuffer b = null;
        
        for (int i = 0; i < p.length; i++) {
            b = new StringBuffer(p[i].getId());
            
            for (int j = 0; j < b.length(); j++) {
                if (b.charAt(j) == DELIMITER) {
                    b.insert(j, ESCAPE);
                    j++;
                }
            }
            
            sb.append(DELIMITER);
            sb.append(b.toString());
        }
         **/
        
        return sb.toString();
    }
    
    /**
     * Create a string representation of this node. This is simply the label
     * of this node 
     */
    public String toString() {
        return this.label;
    }

    /**
     * Used to recursivly determine the path to this node
     *
     * @param node the node from which to start 
     * @param depth the depth we have traversed sofar
     */
    private JxtaNode[] getPathToRoot(JxtaNode node, int depth) {
        JxtaNode[] nodes = null;

        if (node == null) {
            if (depth > 0) {
                nodes = new JxtaNode[depth];
            }
        } else {
            nodes = getPathToRoot(node.getParent(), ++depth);
            nodes[nodes.length - depth] = node;
        }

        return nodes;
    }

    /**
     * Does this  node contain the indicated node in its child list
     * 
     * @param node the node to check for
     * @return true if this node contains the indicated child, false
     *         otherwise
     */
    public boolean contains(JxtaNode node) {
        return getIndexOf(node) > -1 ? true : false;
    }

    /**
     * Return the image to return for this 
     * JxtaNode
     * 
     * @param selected is the node currently selected
     * @param expanded is the node currently expanded
     * @param hasFocus has the node currently keyboard focus
     */
    public ImageIcon getImage(boolean selected, boolean expanded,
        boolean hasFocus) {
        return this.rootIcon;
    }


    /**
     * Return a description for this node. This node always returns an
     * empty string
     *
     * @return a description for this node
     */
    public String getDescription() {
        return DESCRIPTION;
    }

    /**
     * This method may be called if the node was already added to the tree
     * but was accessed. This implementation does nothing
     * 
     * @return always false
     */
    public boolean informAccessed() {
        return false;
    }

    /**
     * Check whether the status of this node has changed. This implementation
     * always returns false
     */
    public boolean checkStatus() {
        return false;
    }
    
    public int compareTo(Object o) {
        String s = o != null ? o.toString() : "";
        
        return equals(o) ? 0 : toString().compareTo(s);
    }

    public JxtaNode getJxtaNode()
    {
        return this;
    }

    public SelectionNodeType getType()
    {
        return SelectionNodeType.UNKNOWN;
    }
    
    
    
}
